﻿#light

// F# Mandelbrot Set Generator, 11.19.08, Matt Valerio, http://thevalerios.net/matt/

#r "FSharp.PowerPack.dll"

open System.Drawing
open System.Windows.Forms
open Microsoft.FSharp.Math

let ShowForm (f : #Form) =
#if INTERACTIVE
    f.Show()
#endif
#if COMPILED
    Application.Run(f :> Form)
#endif

type BitmapForm =
    inherit Form
    val private pictureBox : PictureBox
    new (size : Size) as this =
        {
            pictureBox = new PictureBox()
        } then
            this.SuspendLayout()
            this.pictureBox.BorderStyle <- System.Windows.Forms.BorderStyle.Fixed3D
            this.pictureBox.Name <- "pictureBox"
            this.pictureBox.Size <- size
            this.pictureBox.Dock <- System.Windows.Forms.DockStyle.Fill
            this.pictureBox.SizeMode <- PictureBoxSizeMode.StretchImage
            this.Controls.Add(this.pictureBox)
            this.Name <- "BitmapForm"
            this.Size <- size
            this.ResumeLayout()
            this.pictureBox.Click.Add (fun _ -> printfn "here" |> ignore)
    member this.Bitmap with set value = this.pictureBox.Image <- value

let SetBitmap (bitmap : #Image) (form : #BitmapForm) =
    form.Bitmap <- bitmap
    form

let MagnitudeSquared (c : complex) =  c.r * c.r + c.i * c.i

type Extents<'a> = { Xmin : 'a; Xmax : 'a; Ymin : 'a; Ymax : 'a }  with
    override t.ToString() = sprintf "(%A,%A)-(%A,%A)" t.Xmin t.Ymin t.Xmax t.Ymax

let inline interp y1 y2 x1 x2 x =
    let ymax = max y1 y2
    let ymin = min y1 y2
    let xmax = max x1 x2
    let xmin = min x1 x2
    ((ymax-ymin)/(xmax-xmin))*(x-xmin)+ymin

let MapX (size : Size) (extents : Extents<float>) x =
    interp extents.Xmax extents.Xmin 0.0 (float size.Width) x
    
let MapY (size : Size) (extents : Extents<float>) y =
    interp extents.Ymax extents.Ymin 0.0 (float size.Height) y

let Mandelbrot (maxIter : int) (c : complex) =
    let rec MandelbrotInner z iter =
        if (iter = maxIter) || (z |> MagnitudeSquared >= 4.0) then iter
        else MandelbrotInner (z*z+c) (iter+1)
    MandelbrotInner c 0

let colorMap = [(0, Color.Black); (3, Color.Red); (6, Color.Blue); (10, Color.Chartreuse);
                (20, Color.Magenta); (50, Color.DarkTurquoise); (100, Color.White)]

let PickColor (map : (int * Color) list) (n : int) =
    let InterpolateColor i1 (c1:Color) i2 (c2:Color) =
        let r = interp (int c1.R) (int c2.R) i1 i2 n
        let g = interp (int c1.G) (int c2.G) i1 i2 n
        let b = interp (int c1.B) (int c2.B) i1 i2 n
        Color.FromArgb(r,g,b)
    let rec PickColorInternal map =
        match map with
        | [] -> Color.Black
        | [(_,c)] -> c
        | (i1,c1)::((i2,c2)::_ as t) -> if (i1 <= n) && (n <= i2) then
                                            InterpolateColor i1 c1 i2 c2
                                        else
                                            PickColorInternal t
    PickColorInternal map

let MakeMandelbrotBitmap (maxIter : int) (size : Size) (extents : Extents<float>) =
    let b = new Bitmap(size.Width, size.Height, Imaging.PixelFormat.Format24bppRgb)
    for w in [0 .. size.Width-1] do
        for h in [0 .. size.Height-1] do
            let x = MapX size extents (float w)
            let y = MapY size extents (float h)
            let c = Complex.Create (x,y)
            b.SetPixel(w, h, c |> Mandelbrot maxIter |> PickColor colorMap)
    b

let MakeForm size =
    let f = new BitmapForm(size)
    f.Text <- "F# Mandelbrot"
    f

let maxIter = 100;

let size = new Size(700,600)

let extents = {Xmin = -2.0; Xmax = 1.0; Ymin = -2.0; Ymax = 2.0}

MakeForm size
|> SetBitmap (MakeMandelbrotBitmap maxIter size extents)
|> ShowForm 